﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Beinhaltet den OpenCL Code der Kernel.
    /// </summary>
    partial class KernelRaySplit
    {
        private const string KERNEL_MAIN = "RaySplit";

        /*************
        *  
        *  Eingabe:
        *  material    Das Objektmaterial.                     [i]
        *  rayDir      Die Strahlenrichtung.                   [i]
        *  normal      Die Normale am Schnittpunkt.            [i]
        *  
        * 
        *  Ausgabe:
        *  reflDir     Die Richtung des refl. Strahls.         [i]
        *  refrDir     Die Richtung des refr. Strahls.         [i]
        *  
        *************/
        private const string KERNEL_SOURCE = @"
             kernel void RaySplit(
                global  read_only float16* material,
                global  read_only float4* rayDir,
                global  read_only float4* normal,

                global  write_only float4* reflDir,
                global  write_only float4* refrDir
            )
            {
                int i = get_global_id(0);
              
                float oldRefraction; 
                float newRefraction;
                int sign;
                float cosRho1;

                float into = dot(normal[i], rayDir[i]);
                 //Prüfe ob die Normale entgegen dem Eingangstrahl gerichtet ist
                if (into < 0.0f) {
                    //Wenn nicht befindet sich der Strahl innerhalb eines Objekts
                    sign = -1.0f; 
                    oldRefraction = material[i].se;
                    newRefraction = 1.0002926f; //Refraktionsindex von Luft;
                } else {
                    //Andernfalls außerhalb
                    sign = 1.0f;
                    oldRefraction = rayDir[i].w;
                    newRefraction = material[i].se;
                }	
                //Berechnung der Richtung des Reflexionsstrahls
                if(material[i].sd > 0.0f){
                    cosRho1 = dot(sign * normal[i], rayDir[i]);
                    float4 tempRay = rayDir[i] - (2.0f * cosRho1 * sign *normal[i]);
                    reflDir[i] = (float4)(tempRay.x, tempRay.y, tempRay.z, newRefraction);
                }
               
                //Berechnung der Richtung des Refraktionsstrahls
                if(material[i].sf > 0.0f){
                    // korrekt wäre  n = oldRefraction/newRefraction das führt jedoch zu einem Verhältnis
                    // größer als 1.0f was zu Ringen führt. Deshalb diese Lösung zur Berechnung des Refraktionsverhältnisses:
                    float n = oldRefraction <= newRefraction ? oldRefraction / newRefraction : newRefraction / oldRefraction;
                    cosRho1 = dot(sign *normal[i], rayDir[i]);
                    float cosRho2 = 1.0f - n * n * (1.0f - cosRho1 * cosRho1);  
                    if (cosRho2 > 0.0f){   
                        refrDir[i] = ((sign * n *cosRho1 - sign * sqrt(cosRho2)) * normal[i]) - (n * rayDir[i]);
                        refrDir[i].w = newRefraction;
                    }
                    //Alternative Erstellung des Transmissionsstrahls als gerade Weiterführung des Eingangsstrahls.
                    //refrDir[i] = (float4)(-rayDir[i].x,-rayDir[i].y,-rayDir[i].z,rayDir[i].w);
                }
            }
            ";
    }
}